﻿
/*============== simpleIRC - http://matthias.van-eeghem.com/ ==============*
 *                                                                         *
 *  File: Update.cs                                                      *
 *  Project: simpleIRC                                                     *
 *  Author(s): Matthias Van Eeghem <matthias@van-eeghem.com>               *
 *                                                                         *
 * ========================================================================*
 *                                                                         *
 *  This program is free software: you can redistribute it and/or modify   *
 *  it under the terms of the GNU General Public License as published by   *
 *  the Free Software Foundation, either version 3 of the License, or      *
 *  (at your option) any later version.                                    *
 *                                                                         *
 *  This program is distributed in the hope that it will be useful,        *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
 *  GNU General Public License for more details.                           *
 *                                                                         *
 *  You should have received a copy of the GNU General Public License      *
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.  *
 *                                                                         *
===========================================================================*/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using System.Xml;

namespace simpleIRC.UI
{
    /// <summary>
    /// This class handles all update related functions of the application.
    /// </summary>

    public class Update
    {
        private MainWindow MainWindow = null;

        /// <summary>
        ///  The constructor of the Update class.
        /// </summary>
        
        public Update(MainWindow parentWindow)
        {
            MainWindow = parentWindow;
        }

        /// <summary>
        /// This method checks if there are any updates available for simpleIRC, and if so, it prompts the user to install them
        /// This method should always run on a different thread than the main thread.
        /// </summary>
        /// <param name="showMessageBoxes">If this parameter is true, this method will also show messages informing the user that there is no update available or that the server couldn't be contacted. This should be set to false if called from the MainWindow constructor.</param>

        public void CheckForUpdates(bool showMessageBoxes = true)
        {
            int Major = 0, Minor = 0;
            this.GetNewVersion(ref Major, ref Minor);

            // Wait until we receive the Major and Minor from the GetNewVersion() method
            while (Major == 0 && Minor == 0) Thread.Sleep(100);

            if (Major != -1 && Minor != -1)
            {
                bool UpdateAvailable = false;

                // Check if the version received is newer than the current application version
                if (Major > (int)Core.eApplicationVersion.Major && Minor == (int)Core.eApplicationVersion.Minor) UpdateAvailable = true;
                if (Major == (int)Core.eApplicationVersion.Major && Minor > (int)Core.eApplicationVersion.Minor) UpdateAvailable = true;
                if (Major > (int)Core.eApplicationVersion.Major && Minor > (int)Core.eApplicationVersion.Minor) UpdateAvailable = true;

                // We have an update available
                if (UpdateAvailable)
                {
                    MainWindow.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate()
                    {
                        // Prompt the user to update
                        MessageBoxResult Result = MessageBox.Show("There is an update (v" + Major.ToString() + "." + Minor.ToString() + ") available for simpleIRC, do you want to update now?", "Update available", MessageBoxButton.YesNo, MessageBoxImage.Question);

                        // The user hit yes
                        if (Result == MessageBoxResult.Yes)
                        {
                            MainWindow.OptionsWindow = new OptionsWindow(MainWindow);
                            MainWindow.OptionsWindow.Show();

                            // Disable the "Check for updates" button while we're already processing an update request
                            MainWindow.OptionsWindow.buttonCheckForUpdates.IsEnabled = false;

                            List<string> Changelog = GetChangelog();
                            MainWindow.OptionsWindow.ChangelogBox.Clear();

                            // Select the Update tab
                            TabItem UpdateTab = (TabItem)MainWindow.OptionsWindow.tabControl.Items.GetItemAt(3);
                            UpdateTab.IsSelected = true;

                            // Append each line in the list to the TextBox
                            foreach (string Line in Changelog)
                            {
                                MainWindow.OptionsWindow.ChangelogBox.AppendText(Line + "\r");
                            }
                        }
                    }));

                    // Download the update
                    DownloadUpdate();
                }

                // No update available
                else
                {
                    if (showMessageBoxes)
                        MainWindow.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate() { MessageBox.Show("You have the latest simpleIRC version.", "No update available", MessageBoxButton.OK, MessageBoxImage.Information); }));
                }
            }

            else
            {
                if (showMessageBoxes)
                    MainWindow.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate() { MessageBox.Show("Failed to connect to the update server, the server could be down or you're not connected to the internet.", "Could not connect to the update server", MessageBoxButton.OK, MessageBoxImage.Error); }));
            }
        }

        /// <summary>
        /// This method returns the latest version of the application, stored in an xml file on the site.
        /// </summary>
        /// <param name="versionMajor">The major version of the application</param>
        /// <param name="versionMinor">The minor version of the application</param>

        private void GetNewVersion(ref int versionMajor, ref int versionMinor)
        {
            try
            {
                XmlReader XMLReader = XmlReader.Create("http://matthias.van-eeghem.com/downloads/simpleIRC/Version.xml");
                XMLReader.MoveToContent();

                while (XMLReader.Read())
                {
                    switch (XMLReader.NodeType)
                    {
                        case XmlNodeType.Text:
                        {
                            if (versionMajor == 0)
                            {
                                versionMajor = Convert.ToInt32(XMLReader.Value);
                            }
                            else
                            {
                                versionMinor = Convert.ToInt32(XMLReader.Value);
                            }
                            break;
                        }
                    }
                }
            }
            catch { versionMajor = -1; versionMinor = -1; }
        }

        /// <summary>
        /// This method returns a string list holding each line of the changelog in the Changelog.xml file on the site
        /// </summary>
        /// <returns>Returns the string list</returns>

        private List<string> GetChangelog()
        {
            List<string> Changelog = new List<string>();

            try
            {
                XmlReader XMLReader = XmlReader.Create("http://matthias.van-eeghem.com/downloads/simpleIRC/Changelog.xml");
                XMLReader.MoveToContent();
    
                while (XMLReader.Read())
                {
                    switch (XMLReader.NodeType)
                    {
                        case XmlNodeType.Text:
                        {
                            string[] aLines = XMLReader.Value.Split('\n');
                            foreach (string sLine in aLines)
                            {
                                Changelog.Add(sLine);
                            }
                        }
                        break;
                    }
                }
            }
            catch { }

            return Changelog;
        }

        /// <summary>
        /// This method processes the downloading part of the update process on a background thread
        /// </summary>

        private void DownloadUpdate()
        {
            string DownloadLocation = "http://matthias.van-eeghem.com/downloads/simpleIRC/setup.exe";
            // The path to write the file to, this is the current simpleIRC.exe location
            string DownloadDestination = AppDomain.CurrentDomain.BaseDirectory + "\\setup.exe";

            // Get the size in bytes of the updater
            Uri DownloadUrl = new Uri(DownloadLocation);

            System.Net.HttpWebRequest HttpRequest = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(DownloadUrl);
            System.Net.HttpWebResponse HttpResponse = (System.Net.HttpWebResponse)HttpRequest.GetResponse();
            HttpResponse.Close();

            Int64 DownloadSize = HttpResponse.ContentLength;
            Int64 DownloadedBytes = 0;

            System.Net.WebClient WebClient = new System.Net.WebClient();
            System.IO.Stream RemoteStream = WebClient.OpenRead(new Uri(DownloadLocation));
            Stream streamLocal = new FileStream(DownloadDestination, FileMode.Create, FileAccess.Write, FileShare.None);
            
            int iByteSize = 0;

            // Initialize the buffer array
            byte[] byteBuffer = new byte[DownloadSize];

            // Read the byte size into the variable
            while ((iByteSize = RemoteStream.Read(byteBuffer, 0, byteBuffer.Length)) > 0)
            {
                // Write the bytes to the file system at the file path specified
                streamLocal.Write(byteBuffer, 0, iByteSize);
                DownloadedBytes += iByteSize;

                // Calculate how much percent we've downloaded
                double dIndex = (double)(DownloadedBytes);
                double dTotal = (double)byteBuffer.Length;
                double dProgressPercentage = (dIndex / dTotal);
                int iProgressPercentage = (int)(dProgressPercentage * 100);

                // Convert the bytes integer to kilobytes
                string sTotal = String.Format("{0:0,0}", dTotal / 1024);
                string sIndex = String.Format("{0:0,0}", dIndex / 1024);

                // Update the progress bar and the label
                // As this function is called from a different thread than the main thread we have to invoke the dispatcher
                MainWindow.OptionsWindow.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate()
                {
                    MainWindow.OptionsWindow.progressBar.Value = iProgressPercentage;
                    MainWindow.OptionsWindow.DownloadInfo.Content = sIndex + "/" + sTotal + " kb downloaded";
                }));
            }

            streamLocal.Close();
            RemoteStream.Close();

            MainWindow.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate()
            {
                // Call it from the main thread so the MessageBox is on top of the application
                DownloadFinished();
            }));
        }

        /// <summary>
        /// This method is called when the download finishes.
        /// </summary>

        private void DownloadFinished()
        {
            MainWindow.OptionsWindow.Visibility = Visibility.Hidden;
            // Re-enable the Check for updates button
            MainWindow.OptionsWindow.buttonCheckForUpdates.IsEnabled = true;

            MessageBoxResult Result = MessageBox.Show("The application has finished downloading the update for simpleIRC. Do you wish to update now? You can always manually update afterwards.", "Download finished", MessageBoxButton.YesNo, MessageBoxImage.Information);

            // If the user hit yes, we start the installer
            if (Result == MessageBoxResult.Yes)
            {
                MainWindow.ForceClose = true;

                Process.Start("setup.exe");
                MainWindow.Close();
            }
            
            MainWindow.OptionsWindow.Close();
        }
    }
}
